{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Ch `05`: Concept `03`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Self-organizing map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Import TensorFlow and NumPy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import tensorflow as tf\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Define a class called `SOM`. The constructor builds a grid of nodes, and also defines some helper ops:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "class SOM:\n",
    "    \n",
    "    def __init__(self, width, height, dim):\n",
    "        self.num_iters = 100\n",
    "        self.width = width\n",
    "        self.height = height\n",
    "        self.dim = dim\n",
    "        self.node_locs = self.get_locs()\n",
    "\n",
    "        # Each node is a vector of dimension `dim`\n",
    "        # For a 2D grid, there are `width * height` nodes\n",
    "        nodes = tf.Variable(tf.random_normal([width*height, dim]))\n",
    "        self.nodes = nodes\n",
    "\n",
    "        # These two ops are inputs at each iteration\n",
    "        x = tf.placeholder(tf.float32, [dim])\n",
    "        iter = tf.placeholder(tf.float32)\n",
    "\n",
    "        self.x = x\n",
    "        self.iter = iter\n",
    "\n",
    "        # Find the node that matches closest to the input\n",
    "        bmu_loc = self.get_bmu_loc(x)\n",
    "\n",
    "        self.propagate_nodes = self.get_propagation(bmu_loc, x, iter)\n",
    "        \n",
    "    def get_propagation(self, bmu_loc, x, iter):\n",
    "        '''\n",
    "        Define the weight propagation function that will update weights of the best matching unit (BMU). \n",
    "        The intensity of weight updates decreases over time, as dictated by the `iter` variable.\n",
    "        '''\n",
    "        num_nodes = self.width * self.height\n",
    "        rate = 1.0 - tf.div(iter, self.num_iters)\n",
    "        alpha = rate * 0.5\n",
    "        sigma = rate * tf.to_float(tf.maximum(self.width, self.height)) / 2.\n",
    "        expanded_bmu_loc = tf.expand_dims(tf.to_float(bmu_loc), 0)\n",
    "        sqr_dists_from_bmu = tf.reduce_sum(tf.square(tf.subtract(expanded_bmu_loc, self.node_locs)), 1)\n",
    "        neigh_factor = tf.exp(-tf.div(sqr_dists_from_bmu, 2 * tf.square(sigma)))\n",
    "        rate = tf.multiply(alpha, neigh_factor)\n",
    "        rate_factor = tf.stack([tf.tile(tf.slice(rate, [i], [1]), [self.dim]) for i in range(num_nodes)])\n",
    "        nodes_diff = tf.multiply(rate_factor, tf.subtract(tf.stack([x for i in range(num_nodes)]), self.nodes))\n",
    "        update_nodes = tf.add(self.nodes, nodes_diff)\n",
    "        return tf.assign(self.nodes, update_nodes)\n",
    "    \n",
    "    def get_bmu_loc(self, x):\n",
    "        '''\n",
    "        Define a helper function to located the BMU:\n",
    "        '''\n",
    "        expanded_x = tf.expand_dims(x, 0)\n",
    "        sqr_diff = tf.square(tf.subtract(expanded_x, self.nodes))\n",
    "        dists = tf.reduce_sum(sqr_diff, 1)\n",
    "        bmu_idx = tf.argmin(dists, 0)\n",
    "        bmu_loc = tf.stack([tf.mod(bmu_idx, self.width), tf.div(bmu_idx, self.width)])\n",
    "        return bmu_loc\n",
    "    \n",
    "    def get_locs(self):\n",
    "        '''\n",
    "        Build a grid of nodes:\n",
    "        '''\n",
    "        locs = [[x, y]\n",
    "            for y in range(self.height)\n",
    "            for x in range(self.width)]\n",
    "        return tf.to_float(locs)\n",
    "    \n",
    "    def train(self, data):\n",
    "        '''\n",
    "        Define a function to training the SOM on a given dataset:\n",
    "        '''\n",
    "        with tf.Session() as sess:\n",
    "            sess.run(tf.global_variables_initializer())\n",
    "            for i in range(self.num_iters):\n",
    "                for data_x in data:\n",
    "                    sess.run(self.propagate_nodes, feed_dict={self.x: data_x, self.iter: i})\n",
    "            centroid_grid = [[] for i in range(self.width)]\n",
    "            self.nodes_val = list(sess.run(self.nodes))\n",
    "            self.locs_val = list(sess.run(self.node_locs))\n",
    "            for i, l in enumerate(self.locs_val):\n",
    "                centroid_grid[int(l[0])].append(self.nodes_val[i])\n",
    "            self.centroid_grid = centroid_grid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Time to use our newfound powers. Let's test it out on some data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nB7CFRUAGMtMCAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f514b45fc50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "colors = np.array(\n",
    "     [[0., 0., 1.],\n",
    "      [0., 0., 0.95],\n",
    "      [0., 0.05, 1.],\n",
    "      [0., 1., 0.],\n",
    "      [0., 0.95, 0.],\n",
    "      [0., 1, 0.05],\n",
    "      [1., 0., 0.],\n",
    "      [1., 0.05, 0.],\n",
    "      [1., 0., 0.05],\n",
    "      [1., 1., 0.]])\n",
    "\n",
    "som = SOM(4, 4, 3)\n",
    "som.train(colors)\n",
    "\n",
    "plt.imshow(som.centroid_grid)\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
